import hashlib
import time

from flask import Flask, url_for, request, Blueprint, render_template, send_from_directory, flash, jsonify, redirect
from markupsafe import escape
from werkzeug.security import generate_password_hash

from flaskr import *
from flaskr.db import get_db
from flaskr import user
from flaskr import model

bp = Blueprint('firmware', __name__, url_prefix='/firmware')


def get_firmware_files_prefix_dir():
    return 'firmwares'

def get_firmware_files_dir():
    from flaskr import get_instance_path
    return os.path.join(get_instance_path(), get_firmware_files_prefix_dir())


def get_firmwares(name='', type='', main_no='', sub_no='', status='', version=''):
    db = get_db()
    filters = {}
    filters['name'] = name
    filters['type'] = type
    filters['main_no'] = main_no
    filters['sub_no'] = sub_no
    filters['status'] = status
    filters['version'] = version

    where_names = ''
    where_values = ()
    for k, v in filters.items():
        if v is not None and v != '':
            if where_names == '':
                where_names = ' WHERE {}=?'.format(k)
            else:
                where_names += ' AND {}=?'.format(k)
            where_values += (v,)

    query_str = "SELECT * FROM firmwares{}".format(where_names)
    rs = db.execute(query_str, where_values).fetchall()

    firmwares = []
    for r in rs:
        firmware = {}
        firmware['id'] = r['id']
        firmware['name'] = r['name']
        firmware['type'] = r['type']
        firmware['main_no'] = r['main_no']
        firmware['sub_no'] = r['sub_no']
        firmware['status'] = r['status']
        firmware['version'] = r['version']
        firmware['file_name'] = r['file_name']
        firmware['file_checksum'] = r['file_checksum']
        firmwares.append(firmware)

    return firmwares


def add_firmware(name, type, main_no, sub_no, status, version, file_name, file_checksum):
    db = get_db()
    db.execute(
        'INSERT INTO firmwares (name, type, main_no, sub_no, status, version, file_name, file_checksum) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
        (name, type, main_no, sub_no, status, version, file_name, file_checksum))
    db.commit()

def remove_firmware(id):
    db = get_db()
    r = db.execute('SELECT file_name FROM firmwares WHERE id=?', (id,)).fetchone()
    if r:
        path = os.path.realpath(os.path.join(get_firmware_files_dir(), r['file_name']))
        try:
            os.remove(path)
        except:
            pass
        db.execute('DELETE FROM firmwares WHERE id=?', (id,))
        db.commit()
        return r['file_name']
    return None

def get_firmware(id):
    firmware = {}
    db = get_db()
    r = db.execute('SELECT * FROM firmwares WHERE id=?', (id,)).fetchone()
    if r:
        firmware['id'] = r['id']
        firmware['name'] = r['name']
        firmware['type'] = r['type']
        firmware['main_no'] = r['main_no']
        firmware['sub_no'] = r['sub_no']
        firmware['status'] = r['status']
        firmware['version'] = r['version']
        firmware['file_name'] = r['file_name']
        firmware['file_checksum'] = r['file_checksum']
    return firmware

def get_firmware_types():
    types = []
    db = get_db()
    rs = db.execute('SELECT * FROM firmware_type').fetchall()
    for r in rs:
        types.append(r['type'])
    return types

def get_firmware_statuses():
    statuses = []
    db = get_db()
    rs = db.execute('SELECT * FROM firmware_status').fetchall()
    for r in rs:
        statuses.append(r['status'])
    return statuses


def send_firmware(firmware_name, file_name):
    return send_from_directory(os.path.realpath(get_firmware_files_dir()), escape(file_name), attachment_filename=firmware_name+(os.path.splitext(file_name)[1]), as_attachment=True)


@bp.route('/', methods=('get', 'post'))
@bp.route('/entry', methods=('get', 'post'))
@user.login_required_as_super_ret("")
def entry():
    firmwares = []
    if request.method == 'POST':
        action = request.form.get('action')
        main_no = request.form.get('main_no')
        sub_no = request.form.get('sub_no')
        name = request.form.get('name')
        version = request.form.get('version')
        type = request.form.get('type')
        status = request.form.get('status')
        if action == 'query':
            firmwares = get_firmwares(name, type, main_no, sub_no, status, version)
        elif action == 'upload':
            if (main_no != ''
                    and sub_no != ''
                    and name != ''
                    and version != ''
                    and type != ''
                    and status != ''):
                firmwares = get_firmwares(name, type, main_no, sub_no, status, version)
                if firmwares:
                    flash('Upload failed: firmware is exist.')
                else:
                    file = request.files['file']

                    timestamp = time.time()
                    file_name = '{}.{}'.format(str(timestamp), file.filename)
                    file_fullpath = os.path.join(os.path.realpath(get_firmware_files_dir()), file_name)
                    file.save(file_fullpath)
                    with open(file_fullpath, 'rb') as f:
                        file_checksum = hashlib.md5(f.read()).hexdigest()
                    add_firmware(name, type, main_no, sub_no, status, version, file_name, file_checksum)
                    flash('Upload successfully: {} -> {}'.format(name, file_fullpath))
        elif action[:7] == 'delete:':
            id = action[7:]
            file_name = remove_firmware(id)
            flash("Firmware file deleted: {}".format(file_name))
        elif action[:9] == 'download:':
            id = action[9:]
            firmware = get_firmware(id)
            return send_firmware(firmware.get('name'), firmware.get('file_name'))

    all_nos = model.model_all_nos()
    firmware_types = get_firmware_types()
    firmware_statuses = get_firmware_statuses()

    return render_template('/firmware/entry.html',
                           model_nos=all_nos,
                           firmware_types=firmware_types,
                           firmware_statuses=firmware_statuses,
                           firmwares=firmwares)


@bp.route('/download/<int:id>')
@user.login_required
def download(id):
    firmware = get_firmware(id)
    return send_firmware(firmware.get('name'), firmware.get('file_name'))


@bp.route('/simple_query', methods=('post',))
@user.login_required_ret(nologin="failed")
def simple_query():
    main_no = request.form.get('main_no')
    sub_no = request.form.get('sub_no')
    name = request.form.get('name')
    version = request.form.get('version')
    type = request.form.get('type')
    status = request.form.get('status')

    firmwares = get_firmwares(name, type, main_no, sub_no, status, version)
    return jsonify(firmwares)

@bp.route('/simple_query_types', methods=('post', 'get'))
@user.login_required_ret(nologin="failed")
def simple_query_types():
    firmware_types = get_firmware_types()
    return jsonify(firmware_types)

@bp.route('/simple_query_statuses', methods=('post', 'get'))
@user.login_required_ret(nologin="failed")
def simple_query_statuses():
    firmware_statuses = get_firmware_statuses()
    return jsonify(firmware_statuses)

@bp.route('/simple_download/<int:id>')
@user.login_required_ret(nologin="failed")
def simple_download(id):
    firmware = get_firmware(id)
    return send_firmware(firmware.get('name'), firmware.get('file_name'))
